﻿// <copyright file="Fallout3Game.cs" company="SingularityShift">
// Copyright (c) 2009 SingularityShift.com
// </copyright>
// <author>Ben McClure</author>
// <email>ben@singularityshift.com</email>
// <date>2009-08-14</date>
// <summary>An extension of AbstractGame, this describes the game Fallout 3 using Fallout3Mod objects as IMods.</summary>
namespace GameLib.Games.Fallout3
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using Microsoft.Win32;
    using System.Windows.Forms;
    using GameLib.Games;
    using GameLib.Games.Util;

    /// <summary>
    /// Represents the game Fallout 3, retrieving the current program information from the registry and the game's configuration files.
    /// </summary>
    public class Fallout3Game : AbstractGame<Fallout3Mod>
    {
        /// <summary>
        /// Initializes a new instance of the Fallout3Game class.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors", Justification = "These methods are overridden by THIS class; what's the problem?")]
        public Fallout3Game()
        {
            this.ImportInstalledMods();
            this.ImportActiveMods();
        }

        /// <summary>
        /// <para>Gets the location of the plugins.txt file for the game.</para>
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Plugins", Justification = "This is a valid term for the game.")]
        public static string PluginsFile
        {
            get
            {
                return Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\Fallout3\\plugins.txt";
            }
        }
        
        /// <summary>
        /// <para>Gets or sets the Id of the game.</para>
        /// <para>Set is unsupported. This property always returns "Fallout3"</para>
        /// </summary>
        public override string Id
        {
            get
            {
                return "Fallout3";
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// <para>Gets or sets the Title of the game.</para>
        /// <para>Set is unsupported. This property always returns "Fallout 3"</para>
        /// </summary>
        public override string Title
        {
            get
            {
                return "Fallout 3";
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// <para>Gets or sets the game directory</para>
        /// <para>Setting this value is unsupported. It retrieves the current installation directory from the registry.</para>
        /// </summary>
        public override string GameDir
        {
            get
            {
                string regPath = null;
                if (IntPtr.Size == 8)
                {
                    regPath = "SOFTWARE\\Wow6432Node\\Bethesda Softworks\\Fallout3";
                }
                else if (IntPtr.Size == 4)
                {
                    regPath = "SOFTWARE\\Bethesda Softworks\\Fallout3";
                }

                RegistryKey subKey = Registry.LocalMachine.OpenSubKey(regPath);
                if (subKey == null)
                {
                    return string.Empty;
                }

                return (string)subKey.GetValue("Installed Path");
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// <para>Gets or sets the directory for the Exe.</para>
        /// <para>Setting this is unsupported. This always returns the GameDir.</para>
        /// </summary>
        public override string BinDir
        {
            get
            {
                return this.GameDir;
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// <para>Gets or sets the data directory for the game.</para>
        /// <para>Setting this is unsupported. It always points to a Data subdir of the GameDir.</para>
        /// </summary>
        public override string DataDir
        {
            get
            {
                return this.GameDir + "Data";
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// <para>Gets or sets the directory containing game mods.</para>
        /// <para>Setting this value is unsupported; it always points to the DataDir.</para>
        /// </summary>
        public override string ModDir
        {
            get
            {
                return this.DataDir;
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// <para>Gets or sets the name of the game's primary (or selected) executable file.</para>
        /// <para>Setting this value is unsupported. It always points to Fallout3.exe</para>
        /// </summary>
        public override string Exe
        {
            get
            {
                return "Fallout3.exe";
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// <para>Gets or sets the path to the primary configuration file for the game.</para>
        /// <para>Setting this value is unsupported; it always returns the FALLOUT.INI location.</para>
        /// </summary>
        public override string ConfigFile
        {
            get
            {
                return Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) + "\\My Games\\Fallout3\\FALLOUT.INI";
            }

            set
            {
                throw new NotSupportedException();
            }
        }

        /// <summary>
        /// Exports the ActiveMods list to the game, making the PluginsFile match the mods in ActiveMods.
        /// </summary>
        public override void ExportActiveMods()
        {
            ActiveMods.Export(PluginsFile, true);
            OnGameChanged(new GameChangedEventArgs(GameChangeType.ActiveModsExported));
        }

        /// <summary>
        /// Exports the InstalledMods list to the game. This does not add or remove any mods from being installed, but exports the load order and other configuration settings about the mod.
        /// </summary>
        public override void ExportInstalledMods()
        {
            DateTime startDate = new DateTime(2007, 1, 1);
            int increment = 300000;
            this.ExportInstalledMods(startDate, increment);
        }
        
        /// <summary>
        /// Exports the InstalledMods list to the game. This does not add or remove any mods from being installed, but exports the load order and other configuration settings about the mod.
        /// </summary>
        /// <param name="startDate">A DateTime object indicating when the timestamps used for sorting should begin.</param>
        /// <param name="increment">An integer representing the number of milliseconds to increment the startDate for each mod in the list.</param>
        public void ExportInstalledMods(DateTime startDate, int increment)
        {
            this.ExportInstalledMods(startDate, increment, this.ModDir);
        }

        /// <summary>
        /// Exports the InstalledMods list to the game. This does not add or remove any mods from being installed, but exports the load order and other configuration settings about the mod.
        /// </summary>
        /// <param name="startDate">A DateTime object indicating when the timestamps used for sorting should begin.</param>
        /// <param name="increment">An integer representing the number of milliseconds to increment the startDate for each mod in the list.</param>
        /// <param name="dir">The directory to locate the mod files in.</param>
        public void ExportInstalledMods(DateTime startDate, int increment, string dir)
        {
            DateTime thisDate = startDate;
            foreach (Fallout3Mod mod in InstalledMods)
            {
                string modFile;
                if (!string.IsNullOrEmpty(mod.File))
                {
                    modFile = mod.File;
                } else {
                    modFile = mod.Id;
                }
                if ((!Path.IsPathRooted(modFile)) && (!string.IsNullOrEmpty(dir)))
                {
                    modFile = dir + "\\" + modFile;
                }

                if (File.Exists(modFile))
                {
                    File.SetLastWriteTime(modFile, thisDate);
                    thisDate = thisDate.AddMilliseconds(increment);
                }
            }

            OnGameChanged(new GameChangedEventArgs(GameChangeType.InstalledModsExported));
        }

        /// <summary>
        /// Imports the game's active mods from the PluginsFile.
        /// </summary>
        public override void ImportActiveMods()
        {
            if (this.IsInstalled())
            {
                lock (ActiveMods)
                {
                    ActiveMods.Clear();
                    ActiveMods.Import(PluginsFile);
                }

                OnGameChanged(new GameChangedEventArgs(GameChangeType.ActiveModsReset));
            }
        }

        /// <summary>
        /// Imports the game's installed mods from its ModDir.
        /// </summary>
        public override void ImportInstalledMods()
        {
            if (this.IsInstalled())
            {
                lock (InstalledMods)
                {
                    InstalledMods.Clear();
                    InstalledMods.Import(this.ModDir, "*.esm", ModSortTypes.TimeModified);
                    InstalledMods.Import(this.ModDir, "*.esp", ModSortTypes.TimeModified);
                }

                OnGameChanged(new GameChangedEventArgs(GameChangeType.InstalledModsReset));
            }
        }

        /// <summary>
        /// Enables an installed Fallout3Mod, exporting to the game immediately.
        /// </summary>
        /// <param name="mod">The Fallout3Mod to enable. The mod should already be installed.</param>
        public override void EnableMod(Fallout3Mod mod)
        {
            if (!IsActive(mod))
            {
                ActiveMods.Add(mod);
                this.ExportActiveMods();
                OnGameChanged(new GameChangedEventArgs(GameChangeType.ModEnabled, mod));
            }
        }

        /// <summary>
        /// Disables an installed Fallout3Mod, exporting to the game immediately.
        /// </summary>
        /// <param name="mod">The Fallout3Mod to disable. The mod should already be installed.</param>
        public override void DisableMod(Fallout3Mod mod)
        {
            if (IsActive(mod))
            {
                ActiveMods.RemoveMod(mod);
                ExportActiveMods();
                OnGameChanged(new GameChangedEventArgs(GameChangeType.ModDisabled, mod));
            }
        }

        /// <summary>
        /// Gets a value indicating whether Fallout 3 is currently installed on the system.
        /// </summary>
        /// <returns>A bool indicating whether Fallout 3 is installed</returns>
        public override bool IsInstalled()
        {
            if (string.IsNullOrEmpty(this.GameDir))
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Creates a new, blank instance of the Fallout3Mod type.
        /// </summary>
        /// <returns>A new, blank instance of Fallout3Mod</returns>
        public override Fallout3Mod CreateMod()
        {
            return new Fallout3Mod(this);
        }
    }
}